#ifndef PW_PBL_META_INTERVAL_HPP
#define PW_PBL_META_INTERVAL_HPP

#include "Numbers.hpp"
#include <concepts>
#include <array>

namespace pw_pbl
{
	template <typename T, bool HalfOpen>
	struct TInterval
	{
		T left;
		T right;

		template <typename OT, bool OH> requires((std::integral<T> && std::integral<OT>) || OH == HalfOpen)
		constexpr operator TInterval<OT, OH>() const noexcept
		{
			if constexpr (OH == HalfOpen)
			{
				pw_pbl_assert(fit<OT>());
				return {.left{OT(left)}, .right{OT(right)}};
			}
			else if constexpr (HalfOpen)
			{
				pw_pbl_assert(std::in_range<OT>(left) && right > EMin && std::in_range<OT>(right - 1));
				return {.left{OT(left)}, .right{OT(right - 1)}};
			}
			else
			{
				pw_pbl_assert(std::in_range<OT>(left) && right < EMax && std::in_range<OT>(right + 1));
				return {.left{OT(left)}, .right{OT(right + 1)}};
			}
		}

		template <typename OT>
		constexpr TInterval<OT, HalfOpen> to() const noexcept { return {.left{OT(left)}, .right{OT(right)}}; }

		template <typename OT>
		constexpr bool fit() const noexcept { return std::in_range<OT>(left) && std::in_range<OT>(right); }

		constexpr bool include(T v) const noexcept
		{
			if constexpr (HalfOpen)
				return left <= v && v < right;
			else
				return left <= v && v <= right;
		}

		constexpr auto min() const noexcept { return left; }
		constexpr auto max() const noexcept requires(!HalfOpen) || std::integral<T>
		{
			if constexpr (!HalfOpen)
				return right;
			else
				return right - 1;
		}

		static const TInterval Full;
	};
	template <typename T, bool HalfOpen>
	constexpr TInterval<T, HalfOpen> TInterval<T, HalfOpen>::Full{.left = EMin, .right = EMax};

	template <typename T>
	using ClosedInterval = TInterval<T, false>;

	template <typename T>
	using HalfOpen = TInterval<T, true>;

	inline namespace literals
	{
		constexpr struct
		{
			template <typename T>
			static constexpr HalfOpen<T> operator()(T left, T right) { return {.left{std::move(left)}, .right{std::move(right)}}; }

			template <typename T>
			static constexpr ClosedInterval<T> operator[](T left, T right) { return {.left{std::move(left)}, .right{std::move(right)}}; }
		} Interval{};
	} // namespace literals
} // namespace pw_pbl

#endif // PW_PBL_META_INTERVAL_HPP
